#include "GSharpEnum.h"
#include "GSharpPropertyList.h"
#include "Reflection/FunctionList.h"
#include "Reflection/Function.h"
#include "Reflection/TypeInstance.h"

class CScriptEnumInstance: public ITypeInstance
{
protected:
    CGSharpEnum* Type;
public:
    CScriptEnumInstance(CGSharpEnum* InType)
        :Type(InType)
    {
    }

    bool IsForceConvertableTo(CType*Other) override
    {
        if(Type == Other)
        {
            return true;
        }
        return Type->GetValueType()->IsForceConvertableTo(Other);
    }

    virtual uint32_t GetSize() override
    {
        return sizeof(int64_t);
    }

    virtual void InitInstance(void * Memory) override
    {
        new (Memory) int64_t(0);
    }

    virtual void EraseInstance(void * Instance) override
    {
        *(int64_t*)Instance=0;
    }

    virtual void CloneInstance(const void * Instance, void * TargetMemory) override
    {
        *(int64_t*)TargetMemory = *(const int64_t*)Instance;
    }

    virtual CString ToDebugString(const void * InInstance) const override
    {
        //find value name
        if(!Type->IsValidValue(*(int64_t*)InInstance))
        {
            return Type->GetFullName()+U"//"+U"InvalidValue("+CString(*(int64_t*)InInstance)+U")";
        }
        
        return Type->GetFullName()+U"//"+Type->GetValueName(*(int64_t*)InInstance);
    }

    virtual uint64_t GetInstanceHash(const void *InInstance) const override
    {
        return *(int64_t*)InInstance;
    }

    virtual bool IsInstanceEqual(const void *A, const void *B) const override
    {
        return *(int64_t*)A==*(int64_t*)B;
    }

    virtual void *CreateInstance() override
    {
        return new int64_t(0);
    }

    virtual void ResetAllocated(void *Memory) override
    {
        *(int64_t*)Memory=0;
    }

};

CGSharpEnum::CGSharpEnum(
        STypeID InTypeID
        ,const TMap<int64_t,CString>& InValues)
    :CEnumBase(
        InValues
        ,InTypeID
        ,ETypeFlags::Script
    )
{
    Instancable= new CScriptEnumInstance(this);
    Properties=new CGSharpPropertyList(this);
    Functions= new CFunctionList(this);

    PostConstructFunction = [this]()
    {
        OnPostConstruct(this);
    };
}

CGSharpEnum::~CGSharpEnum()
{
    if(StaticFieldValues)
    {
        delete StaticFieldValues;
    }
}

void CGSharpEnum::OnPostConstruct(CGSharpEnum* InEnum)
{
    assert(InEnum->Properties->IsScriptPropertyList());
    CGSharpPropertyList * GSharpFields = static_cast<CGSharpPropertyList*>(InEnum->Properties);
    InEnum->StaticFieldValues=GSharpFields->CreateStaticFieldValues();

    //invoke static constructor if exist
    for(auto & Function: InEnum->Functions->Functions)
    {
        if(Function->IsStatic()
            && Function->GetName()==InEnum->GetShortName())
        {
            TVector<CAny> Args;
            Function->Invoke(Args);
            break;
        }
    }

}

CGSharpFieldValuesContainer *CGSharpEnum::GetStaticValues() const
{
    return StaticFieldValues;
}
